package main

import (
	"database/sql"
	"fmt"
	"strings"
	_ "github.com/go-sql-driver/mysql"
)

type Agency struct {
	Id int32
	//ParentId int32
	Title string
	Lft int32
	Rgt int32
}

//数据库配置
const (
	userName = "root"
	password = "123456"
	ip = "127.0.0.1"
	port = "3306"
	dbName = "my_test"
)

// Db数据库连接池
var DB *sql.DB
// 注意方法名大写，就是public
func InitDB() {
	//构建连接："用户名:密码@tcp(IP:端口)/数据库?charset=utf8"
	path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
	fmt.Println(path)
	//打开数据库,前者是驱动名，所以要导入： _ "github.com/go-sql-driver/mysql"
	DB, _ = sql.Open("mysql", path)
	if DB == nil {
		fmt.Println("连接失败！")
		return
	}

	//设置数据库最大连接数
	DB.SetConnMaxLifetime(10)
	//设置上数据库最大闲置连接数
	DB.SetMaxIdleConns(5)
	//验证连接
	if err := DB.Ping(); err != nil{
		fmt.Println("opon database fail")
		return
	}
	fmt.Println("connnect success")
}

/**
 * 新建节点
 * @param ag Agency
 * @param pId int32	父级节点ID
 */
func InsertAgency(ag Agency, pId int32) (bool){
	//开启事务
	tx, err := DB.Begin()
	if err != nil{
		fmt.Println("tx fail")
		return false
	}

	// 获取父级节点：左右数据
	var pAg Agency
	err = DB.QueryRow("SELECT id, lft, rgt FROM t_agency_lr WHERE id = ?", pId).Scan(&pAg.Id, &pAg.Lft, &pAg.Rgt)
	//err = DB.QueryRow("SELECT * FROM t_agency_lr WHERE id = ?", pId).Scan(&pAg.Id, &pAg.Lft, &pAg.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}

	// 更新左值大于该父节点左值的值
	agmt, err := tx.Prepare("UPDATE t_agency_lr SET lft = lft + 2 WHERE lft > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	//设置参数以及执行sql语句
	res, err := agmt.Exec(pAg.Lft)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 更新右值大于该父节点左值的值
	agmt, err = tx.Prepare("UPDATE t_agency_lr SET rgt = rgt + 2 WHERE rgt > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	// 设置参数以及执行sql语句
	res, err = agmt.Exec(pAg.Lft)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 新增节点数据
	stmt, err := tx.Prepare("INSERT INTO t_agency_lr (`id`,`title`,`lft`, `rgt`) VALUES (?, ?, ?, ?)")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	// 将参数传递到sql语句中并且执行:父节点右值为新节点的左值，新节点的右值需要再左节点的基础上加1
	res, err = stmt.Exec(ag.Id, ag.Title, pAg.Lft+1, pAg.Lft+2)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	//将事务提交
	tx.Commit()
	//获得上一个插入自增的id
	fmt.Println(res.LastInsertId())
	return true
}

/**
 * 删除节点
 * @param agId int32	需要的节点ID
 */
func DeleteAgency(agId int32) (bool) {
	//开启事务
	tx, err := DB.Begin()
	if err != nil{
		fmt.Println("tx fail")
	}

	// 获取父级：左右数据
	var ag Agency
	err = DB.QueryRow("SELECT id, lft, rgt FROM t_agency_lr WHERE id = ?", agId).Scan(&ag.Id, &ag.Lft, &ag.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}
	// 获取节点数层
	dis := ag.Rgt - ag.Lft + 1

	// 删除左值在lft和rgt之间的结点，包括自己
	stmt, err := tx.Prepare("DELETE FROM t_agency_lr WHERE lft >= ? AND lft<= ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err := stmt.Exec(&ag.Lft, &ag.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 更新左边值
	agmt, err := tx.Prepare("UPDATE t_agency_lr SET lft=lft - ? WHERE lft > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err = agmt.Exec(dis, ag.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}
	// 更新右边值
	agmt, err = tx.Prepare("UPDATE t_agency_lr SET rgt=rgt - ? WHERE rgt > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err = agmt.Exec(dis, ag.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	//提交事务
	tx.Commit()
	//获得上一个insert的id
	fmt.Println(res.LastInsertId())
	return true
}

/**
 * 更新节点,只支持单一节点更改，节点下没有节点: 多节点存在逻辑不通问题 TODO
 * @param newId int32	需要插入的新节点下的ID
 * @param oldId int32	原有节点的ID
 */
func UpdateAgency(newId int32, oldId int32) (bool) {
	// 开启事务
	tx, err := DB.Begin()
	if err != nil{
		fmt.Println("tx fail")
	}

	// 获取新节点：左右数据
	var agNew Agency
	err = DB.QueryRow("SELECT id, lft, rgt FROM t_agency_lr WHERE id = ?", newId).Scan(&agNew.Id, &agNew.Lft, &agNew.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}
	// 获取旧节点：左右数据
	var agOld Agency
	err = DB.QueryRow("SELECT id, lft, rgt FROM t_agency_lr WHERE id = ?", oldId).Scan(&agOld.Id, &agOld.Lft, &agOld.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}
	// 判断旧节点是是否有子节点，有子节点不支持修改
	if agOld.Rgt - agOld.Lft > 1 {
		return false
	}
	// 获取旧节点数层
	dis := agOld.Rgt - agOld.Lft + 1
	// 获取旧节点下的所有节点ID，L, R
	oldRows, err := DB.Query("SELECT id, lft, rgt from t_agency_lr WHERE lft >= ? AND rgt <= ?", agOld.Lft, agOld.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}
	// 获取原节点的数据
	var ags []Agency
	for oldRows.Next() {
		var ag Agency
		// 将每一行的结果都赋值到一个对象中
		err := oldRows.Scan(&ag.Id, &ag.Lft, &ag.Rgt)
		if err != nil {
			fmt.Println("rows fail")
		}
		ags = append(ags, ag)
		// 判断旧节点是是否包含新节点，包含就不能修改，存在树节点跟随问题
		//if ag.Id == agNew.Id || ag.Id == agOld.Id{
		if ag.Id == agNew.Id{
			return false
		}
	}

	// 去掉原节点数据，保持节点一致性
	// 撤销原节点值：排除本节点数据，更新左边值
	agmt, err := tx.Prepare("UPDATE t_agency_lr SET lft=lft - ? WHERE lft > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err := agmt.Exec(dis, agOld.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}
	// 撤销原节点值：排除本节点数据，更新右边值
	agmtu, err := tx.Prepare("UPDATE t_agency_lr SET rgt=rgt - ? WHERE rgt > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err = agmtu.Exec(dis, agOld.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 将新节点的数据，腾出空间给旧节点，更新左值
	agmtn, err := tx.Prepare("UPDATE t_agency_lr SET lft=lft + ? WHERE Lft > ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err = agmtn.Exec(dis, agNew.Lft)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}
	// 将新节点的数据，腾出空间给旧节点，更新右值7
	agmtu, err = tx.Prepare("UPDATE t_agency_lr SET rgt=rgt + ? WHERE rgt >= ?")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	res, err = agmtu.Exec(dis, agNew.Rgt)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 将原节点的数据，修改到新节点下去，更新左右值
	agmtEnd, err := tx.Prepare("UPDATE t_agency_lr SET lft = ?, rgt = ? WHERE id = ? ")
	if err != nil{
		fmt.Println("Prepare fail")
		return false
	}
	// 只追加一个节点，则左侧加1，右侧加2
	res, err = agmtEnd.Exec(agNew.Lft + 1, agNew.Lft + 2, &agOld.Id)
	if err != nil{
		fmt.Println("Exec fail")
		return false
	}

	// 提交事务
	tx.Commit()
	fmt.Println(res.LastInsertId())
	return true
}

// 查询一行数据
func SelectAgencyById(id int) (Agency) {
	var ag Agency
	err := DB.QueryRow("SELECT * FROM t_agency_lr WHERE id = ?", id).Scan(&ag.Id, &ag.Title, &ag.Lft, &ag.Rgt)
	if err != nil{
		fmt.Println("查询出错了")
	}
	return ag
}

// 查询多行数据
func SelectAllAgency() ([]Agency) {
	// 执行查询语句
	rows, err := DB.Query("SELECT * from t_agency_lr")
	if err != nil{
		fmt.Println("查询出错了")
	}

	var ags []Agency
	// 循环读取结果
	for rows.Next(){
		var ag Agency
		//将每一行的结果都赋值到一个user对象中
		err := rows.Scan(&ag.Id, &ag.Title, &ag.Lft, &ag.Rgt)
		if err != nil {
			fmt.Println("rows fail")
		}

		// 将user追加到users的这个数组中
		ags = append(ags, ag)
	}
	return ags
}

func main() {
	InitDB()
	ag := Agency{
		//Id: 11,
		Title: "xxwaaaaaaa",
		Lft: 20,
		Rgt: 55,
	}
	fmt.Println(ag)

	// 注释的操作自己选择进行
	InsertAgency(ag, 3)

	//DeleteAgency(20)

	// 只支持旧节点下没有节点，备注：由于修改多节点，存在断链归属逻辑问题，因此不做处理
	//UpdateAgency(2, 8)

	agencytemp := SelectAgencyById(1)
	fmt.Println(agencytemp)

	ags := SelectAllAgency()
	fmt.Println(ags)
}